﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;
using System.Collections;
using System.Text.RegularExpressions;

namespace SolrExample.Common
{
    public class IndexFieldFactory
    {
        public static string ObjectToXml<T>(T obj)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            StringBuilder sbXml = new StringBuilder();

            serializer.Serialize(new StringWriter(sbXml), obj);
            return sbXml.ToString();
        }

        public static T XmlToObject<T>(string xml)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            XmlTextReader reader = new XmlTextReader(new StringReader(xml));

            T obj = (T)serializer.Deserialize(reader);
            return obj;
        }

        public static Dictionary<string, string> ComposeIndexFieldXml<TIndexField>(IList<TIndexField> indexFields, bool includeAddNode, string jobsDBTextFields)
        {
            Dictionary<string, string> result = GetDocList(indexFields, jobsDBTextFields);

            if (includeAddNode)
            {
                List<string> countryCodes = result.Keys.ToList();
                foreach (string countryCode in countryCodes)
                {
                    result[countryCode] = new StringBuilder().Append("<?xml version='1.0' encoding='UTF-8' ?><add overwrite=\"true\">")
                                                                .Append(result[countryCode])
                                                                .Append("</add>").ToString();
                }
            }

            return result;
        }

        public static Dictionary<string, string> GetDocList<TIndexField>(IList<TIndexField> indexFields, string jobsDBTextFieldsStr)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            List<string> jobsDBTextFields = jobsDBTextFieldsStr.Split(new char[] { ',' }).ToList();

            //get all properties from TIndexField and base interface
            PropertyInfo[] instancePropertyInfos = typeof(TIndexField).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);

            Type[] interfaceTypes = typeof(TIndexField).GetInterfaces();
            List<PropertyInfo> allPropertyInfos = new List<PropertyInfo>();

            foreach (Type type in interfaceTypes)
            {
                allPropertyInfos.AddRange(type.GetProperties());
            }

            allPropertyInfos.AddRange(instancePropertyInfos);

            List<PropertyInfo> distinctPropertyInfos = new List<PropertyInfo>();
            foreach (PropertyInfo pi in allPropertyInfos)
            {
                var piCurrent = distinctPropertyInfos.SingleOrDefault(i => i.Name.Equals(pi.Name, StringComparison.OrdinalIgnoreCase));
                if (null == piCurrent)
                {
                    distinctPropertyInfos.Add(pi);
                }
            }

            foreach (var indexField in indexFields)
            {
                StringBuilder sbXml = new StringBuilder();
                string countryCode = string.Empty;
                bool isSearchableRecord = true;

                sbXml.Append("<doc>");
                foreach (PropertyInfo pi in distinctPropertyInfos)
                {
                    if (pi.PropertyType.IsValueType
                            || pi.PropertyType == typeof(string)
                            || (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
                    {
                        var piValue = pi.GetValue(indexField, null);
                        string piName = pi.Name.ToLower();
                        bool isSearchableRecordField = pi.Name.Equals("IsSearchableRecord", StringComparison.OrdinalIgnoreCase);

                        if (isSearchableRecordField)
                        {
                            isSearchableRecord = Convert.ToBoolean(piValue);
                            if (!isSearchableRecord)
                            {
                                break;
                            }
                        }

                        if (!isSearchableRecordField && piValue != null)
                        {
                            if ("CountryCode".Equals(pi.Name, StringComparison.OrdinalIgnoreCase))
                            {
                                countryCode = piValue.ToString();
                                if (!result.ContainsKey(countryCode))
                                {
                                    result[countryCode] = string.Empty;
                                }
                            }

                            //string jobsdb_text = CommonConfig.GetConfigValue(piName);
                            if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(Nullable<DateTime>))
                            {
                                string dateValue = string.Format("{0:yyyy-MM-ddTHH:mm:ss.fffffff}Z", piValue);
                                sbXml.Append("<field name=\"" + piName + "\">").Append(dateValue).Append("</field>");
                            }
                            else if (pi.PropertyType == typeof(String))
                            {
                                string fieldValue = piValue.ToString().Trim();
                                if (jobsDBTextFields.Contains(piName))
                                {
                                    string pattern = "[\\x00-\\x09\\x0b-\\x0c\\x0e-\\x1f]";
                                    fieldValue = Regex.Replace(fieldValue, pattern, string.Empty); //replace special invisible chars
                                }

                                // prevents the field value contains cdata end tag, and breaks the xml document generated
                                // some job ad records really contains this char pattern
                                if (fieldValue.Contains("]]>"))
                                {
                                    fieldValue = fieldValue.Replace("]]>", "]]]]><![CDATA[>");
                                }

                                sbXml.Append("<field name=\"" + piName + "\">")
                                        .Append("<![CDATA[" + fieldValue + "]]>")
                                        .Append("</field>");
                            }
                            else if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
                            {
                                //var enumerator = ((IEnumerable)piValue).GetEnumerator();
                                //enumerator.Reset();
                                //while (enumerator.MoveNext())
                                //{
                                //    sbXml.Append("<field name=\"" + piName + "\">").Append(enumerator.Current).Append("</field>");
                                //}

                                IList objList = (IList)piValue;
                                foreach (var obj in objList)
                                {
                                    sbXml.Append("<field name=\"" + piName + "\">").Append(obj).Append("</field>");
                                }
                            }
                            else
                            {
                                sbXml.Append("<field name=\"" + piName + "\">").Append(piValue.ToString().Trim()).Append("</field>");
                            }
                        }
                    }
                }

                if (isSearchableRecord)
                {
                    sbXml.Append("</doc>");
                    if (result.ContainsKey(countryCode))
                    {
                        result[countryCode] += sbXml.ToString();
                    }
                }
            }

            List<string> countryCodes = result.Keys.ToList();
            foreach (string countryCode in countryCodes)
            {
                if (string.IsNullOrEmpty(result[countryCode]))
                {
                    result.Remove(countryCode);
                }
            }

            return result;
        }




    }
}
